package cn.donting.jme.expansion.tool.transform;

import cn.donting.jme.EditorContent;
import cn.donting.jme.core.input.InputRaw;
import cn.donting.jme.editor.scene.SceneCamera;
import cn.donting.jme.expansion.tool.AbsSceneTool;
import cn.donting.jme.utile.MathUtil;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.MouseInput;
import com.jme3.material.Material;
import com.jme3.material.Materials;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class TransformSceneTool extends AbsSceneTool {
    protected boolean moving = false;
    private Vector3f startTransformPlanPos;
    protected Axis moveAxis;

    private Geometry moveAxisMeshGeometry;
    private Geometry xAxisGeometry;
    private Geometry yAxisGeometry;
    private Geometry zAxisGeometry;
    public static final ColorRGBA xAxisColor = new ColorRGBA(0.8235294f, 0.0f, 0.0f, 1.0f);
    public static final ColorRGBA zAxisColor = new ColorRGBA(0.0f, 0.0f, 0.8235294f, 1.0f);
    public static final ColorRGBA yAxisColor = new ColorRGBA(0.0f, 0.8235294f, 0.0f, 1.0f);
    public static final ColorRGBA axisColorTransparent = ColorRGBA.White.clone();


    protected void followTarget(Spatial transformSpa) {
        Spatial selectSpatial = getSelectSpatial();
        SceneCamera selectCamera = getSelectCamera();
        if (selectSpatial != null) {
            transformSpa.setCullHint(Spatial.CullHint.Never);
            Vector3f worldTranslation = selectSpatial.getWorldTranslation();
            Quaternion worldRotation = selectSpatial.getWorldRotation();
            transformSpa.setLocalRotation(worldRotation);
            transformSpa.setLocalTranslation(worldTranslation);
            Camera camera = EditorContent.getJmeApplication().getEditorCamera();
            Vector3f location = camera.getLocation();
            float distance = worldTranslation
                    .distance(location);
            transformSpa.setLocalScale(distance / 10);
        } else if (selectCamera != null) {
            transformSpa.setCullHint(Spatial.CullHint.Never);
            Vector3f worldTranslation = selectCamera.getCamera().getLocation();
            Quaternion worldRotation = selectCamera.getCamera().getRotation();
            transformSpa.setLocalRotation(worldRotation);
            transformSpa.setLocalTranslation(worldTranslation);
            Camera camera = EditorContent.getJmeApplication().getEditorCamera();
            Vector3f location = camera.getLocation();
            float distance = worldTranslation
                    .distance(location);
            transformSpa.setLocalScale(distance / 10);
        } else {
            transformSpa.setCullHint(Spatial.CullHint.Always);
        }
    }

    @Override
    public void update(float tpf) {
        initAxisGeometry();
        followTarget(getToolNode());
        if (moving) {
            axisGeometryFollow();
            if (InputRaw.mouseButtonUp(MouseInput.BUTTON_LEFT)) {
                moving = false;
                moveAxis = null;
                getToolNode().detachChild(xAxisGeometry);
                getToolNode().detachChild(yAxisGeometry);
                getToolNode().detachChild(zAxisGeometry);
                setAxisColor(xAxis(), xAxisColor);
                setAxisColor(yAxis(), yAxisColor);
                setAxisColor(zAxis(), zAxisColor);
            } else {
                //TODO:
                CollisionResults mouseSelectGeometry = getMouseSelectGeometry(moveAxisMeshGeometry);
                if (mouseSelectGeometry.size() == 0) {
                    return;
                }
                CollisionResult collision1 = mouseSelectGeometry.getCollision(0);
                Vector3f contactPoint = collision1.getContactPoint();
                if (MathUtil.isNaN(contactPoint)) {
                    return;
                }
                move(moveAxis, startTransformPlanPos, contactPoint);
                return;
            }
        }
        CollisionResults mouseSelectGeometry = getMouseSelectGeometry(getToolNode());
        boolean cursorVisible = EditorContent.getJmeApplication().getInputManager().isCursorVisible();
        if (cursorVisible && mouseSelectGeometry.size() > 0) {
            CollisionResult collision = mouseSelectGeometry.getCollision(0);
            Geometry geometry = collision.getGeometry();
            if (geometry.getName().equals("xAxis")) {
                setAxisColorLog(xAxis(), ColorRGBA.Yellow);
                moveAxis = Axis.X;
            }
            if (geometry.getName().equals("yAxis")) {
                setAxisColorLog(yAxis(), ColorRGBA.Yellow);
                moveAxis = Axis.Y;
            }
            if (geometry.getName().equals("zAxis")) {
                setAxisColorLog(zAxis(), ColorRGBA.Yellow);
                moveAxis = Axis.Z;
            }
            if (InputRaw.mouseButtonDown(MouseInput.BUTTON_LEFT)) {
                moving = true;
                if (moveAxis.equals(Axis.X)) {
                    setAxisColor(yAxis(), axisColorTransparent);
                    setAxisColor(zAxis(), axisColorTransparent);
                    getToolNode().attachChild(xAxisGeometry);
                    moveAxisMeshGeometry = xAxisGeometry;
                }
                if (moveAxis.equals(Axis.Y)) {
                    setAxisColor(xAxis(), axisColorTransparent);
                    setAxisColor(zAxis(), axisColorTransparent);
                    getToolNode().attachChild(yAxisGeometry);
                    moveAxisMeshGeometry = yAxisGeometry;
                }
                if (moveAxis.equals(Axis.Z)) {
                    setAxisColor(xAxis(), axisColorTransparent);
                    setAxisColor(yAxis(), axisColorTransparent);
                    getToolNode().attachChild(zAxisGeometry);
                    moveAxisMeshGeometry = zAxisGeometry;
                }
                CollisionResults mouseSelectGeometry1 = getMouseSelectGeometry(moveAxisMeshGeometry);
                if (mouseSelectGeometry1.size() > 0) {
                    CollisionResult collision1 = mouseSelectGeometry1.getCollision(0);
                    startTransformPlanPos = collision1.getContactPoint();
                }
                startMove();
                axisGeometryFollow();
            }
        } else {
            setAxisColor(xAxis(), xAxisColor);
            setAxisColor(yAxis(), yAxisColor);
            setAxisColor(zAxis(), zAxisColor);
        }
    }

    private void initAxisGeometry() {
        if (xAxisGeometry != null) {
            return;
        }
        Material material = new Material(EditorContent.getJmeApplication().getAssetManager(), Materials.UNSHADED);
        material.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
        material.setColor("Color", new ColorRGBA(6 / 255f, 49 / 255f, 75 / 255f, 1));
        xAxisGeometry = new Geometry();
        xAxisGeometry.setMaterial(material);
        xAxisGeometry.setMesh(new XAxisMesh());
        xAxisGeometry.setCullHint(Spatial.CullHint.Always);

        yAxisGeometry = new Geometry();
        yAxisGeometry.setMaterial(material);
        yAxisGeometry.setMesh(new YAxisMesh());
        yAxisGeometry.setCullHint(Spatial.CullHint.Always);

        zAxisGeometry = new Geometry();
        zAxisGeometry.setMaterial(material);
        zAxisGeometry.setMesh(new ZAxisMesh());
        zAxisGeometry.setCullHint(Spatial.CullHint.Always);

    }

    /**
     * 轴平面跟随旋转
     */
    private void axisGeometryFollow() {
        if (moveAxis == null) {
            return;
        }
        Camera camera = EditorContent.getJmeApplication().getEditorCamera();
        Vector3f cameraPos = camera.getLocation().clone();
        Vector3f cameraLos;
        cameraLos = getToolNode().worldToLocal(cameraPos.clone(), null);
        if (moveAxis.equals(Axis.X)) {
            cameraLos.x = 0;
            Vector3f normal = moveAxis.normal();
            //弧度夹角
            float dot = calculateAngleBetweenVectors(cameraLos, normal);
            if (cameraLos.y < 0) {
                dot = -dot;
            }
            // 创建一个四元数表示绕X轴的旋转
            Quaternion rotation = new Quaternion();
            rotation.fromAngleAxis(-dot, Vector3f.UNIT_X);
            // 应用旋转变换
            xAxisGeometry.setLocalRotation(rotation);
        }
        if (moveAxis.equals(Axis.Y)) {
            cameraLos.y = 0;
            Vector3f normal = moveAxis.normal();
            //弧度夹角
            float dot = calculateAngleBetweenVectors(cameraLos, normal);
            if (cameraLos.z < 0) {
                dot = -dot;
            }
            // 创建一个四元数表示绕X轴的旋转
            Quaternion rotation = new Quaternion();
            rotation.fromAngleAxis(-dot, Vector3f.UNIT_Y);
            // 应用旋转变换
            yAxisGeometry.setLocalRotation(rotation);
        }
        if (moveAxis.equals(Axis.Z)) {
            cameraLos.z = 0;
            Vector3f normal = moveAxis.normal();
            //弧度夹角
            float dot = calculateAngleBetweenVectors(cameraLos, normal);
            if (cameraLos.x < 0) {
                dot = -dot;
            }
            // 创建一个四元数表示绕X轴的旋转
            Quaternion rotation = new Quaternion();
            rotation.fromAngleAxis(-dot, Vector3f.UNIT_Z);
            // 应用旋转变换
            zAxisGeometry.setLocalRotation(rotation);
        }
    }

    private void setAxisColorLog(Spatial spatial, ColorRGBA colorRGBA) {
        if (spatial instanceof Geometry) {
            Material material = ((Geometry) spatial).getMaterial();
            material.setColor("Color", colorRGBA);
        }
        if (spatial instanceof Node) {
            for (Spatial child : ((Node) spatial).getChildren()) {
                setAxisColorLog(child, colorRGBA);
            }
        }
    }

    private void setAxisColor(Spatial spatial, ColorRGBA colorRGBA) {
        if (spatial instanceof Geometry) {
            Material material = ((Geometry) spatial).getMaterial();
            material.setColor("Color", colorRGBA);
        }
        if (spatial instanceof Node) {
            for (Spatial child : ((Node) spatial).getChildren()) {
                setAxisColor(child, colorRGBA);
            }
        }
    }

    /**
     * 计算两个向量的夹角（弧度）
     *
     * @param vectorA
     * @param vectorB
     * @return
     */
    public float calculateAngleBetweenVectors(Vector3f vectorA, Vector3f vectorB) {
        // 计算点积
        float dotProduct = vectorA.dot(vectorB);
        // 计算向量长度
        float lengthA = vectorA.length();
        float lengthB = vectorB.length();
        // 计算夹角的弧度值
        float angleRadians = FastMath.acos(dotProduct / (lengthA * lengthB));

        return angleRadians;
    }

    protected abstract void startMove();

    protected abstract void move(Axis axis, Vector3f startPos, Vector3f endPos);

    protected abstract Spatial xAxis();

    protected abstract Spatial yAxis();

    protected abstract Spatial zAxis();


}
